package tota.tuples;

/**
 * <p>Title: TOTA</p>
 * <p>Description: TOTA</p>
 * <p>Copyright: Copyright (c) 2002</p>
 * <p>Company: DII - DISMI</p>
 * @author Marco Mamei
 * @version 1.0
 */

import java.util.*;
import tota.tuples.message.*;
import tuplespace.tuples.*;
import lime.tuples.*;

public class HopTuple extends StructureTuple
{
	
 private static final String PM = "P3";
 private static final int MIN_TIME = 385;
	
 private static final int DEL_DELAY = 7;
 private static final int MOV_DELAY = 3;
 public int hop = 0;
 
 private boolean delayedDelScheduled = false;
 private boolean delayedMoveScheduled = false;
 
 // this tuple is used to handle predelete state
 private LocalMessage m;
 
 protected void makeSubscriptions() {
  super.makeSubscriptions();
  /* make subscriptions */
  PresenceTuple pres = new PresenceTuple("<peer=*>");
  TsTuple inPres =  new TsTuple("<op=IN><tuple="+pres.serialize()+">");
  tota.subscribe(inPres, this,"PC");

  TsTuple tOut = new TsTuple("<op=OUT><tuple="+this.serialize()+">");
  tota.subscribe(tOut, this,"OUT");
 }

 protected boolean decideEnter() {
  HopTuple prev = (HopTuple)tota.keyrd(this);

  if(prev!=null && prev.delayedDelScheduled)
   return false;

  boolean res = (prev == null || prev.hop > (this.hop + 1));
  
  if(prev!=null && res == true)
   tota.delete(prev); // delete the prev tuple
  
  boolean res2 =  safeEnter();
  
  
  test("ENTER ("+res+" && "+res2+") "+this.serialize());
  testLocal();
  testOneHop();
	      
  return res && res2;
 }

 protected void changeTupleContent()
 {
  hop++;
 }

 protected boolean decidePropagate()
 {
  return (!delayedDelScheduled) && isWorthPropagate();
 }
 
 protected void propagate() {
  move();
 }
 
 private void delete() {
  if(!delayedDelScheduled) {
   delayedDelScheduled = true;
   tota.unsubscribe(new Tuple("<content=*>"), this);
   // local message creates the pre-delete state
   m = new LocalMessage();
   m.setContent("<content="+this.id+">");
   tota.inject(m);
   int currentTime = getTime();
   SensorTuple st = new SensorTuple("<sensor=clock><value="+(currentTime+DEL_DELAY)+">");
   tota.subscribe(st, this,"DELAY_DELETE");
  }
 }
 
 private void move() {
  if(!delayedMoveScheduled) {
   delayedMoveScheduled = true;
   int currentTime = getTime();
   SensorTuple st = new SensorTuple("<sensor=clock><value="+(currentTime+MOV_DELAY)+">");
   tota.subscribe(st, this,"DELAY_MOVE");
  }
 }
 
 
 
 /*****************************************************************************/
 /*                    REACTIVE COMPONENT INTERFACE                           */
 /*****************************************************************************/
 public void react(String reaction, String event) {
  super.react(reaction,event);
  
  if(reaction.equalsIgnoreCase("PC") && !delayedDelScheduled && !delayedMoveScheduled) {
   if(safeState() && decidePropagate())
    move();
  }
  else if(reaction.equalsIgnoreCase("OUT") && !delayedDelScheduled && !delayedMoveScheduled) {    
   TsTuple envelope = (TsTuple)Tuple.deserialize(event);
   TotaTuple t = (TotaTuple)Tuple.deserialize(envelope.tuple);
   /* this is a local event */
   if(t.from.equals(tota.toString()))
    return;
   
   test(tota.toString()+": "+getTime()+" :: "+event);
   
   
   if(!safeState()) {  
    test(tota.toString()+" delete......");
    delete();
   }
   else if(decidePropagate() && isWorthPropagate()) {
    test(tota.toString()+" move......"); 
    move();
   }
   else {
	test("strange......"); 
   }
  }
  
  else if(reaction.equalsIgnoreCase("DELAY_DELETE")) {
   delayedDelScheduled = false;
   tota.unsubscribe(Tuple.deserialize(event),this);
   tota.delete(m);
   tota.delete(this);
   test("Real Delete");
  }
  else if(reaction.equalsIgnoreCase("DELAY_MOVE")) {
   delayedMoveScheduled = false;
   tota.unsubscribe(Tuple.deserialize(event),this);
   tota.move(this);
   test("Real Move"+this.serialize());
  }
 }

 /*****************************************************************************/
 /*                            LOW LEVEL METHODS                              */
 /*****************************************************************************/

 /* This mehod returns a PeerValue array, containing the list of neighboring
 peers together wiht thier gradient values */
 private Object[] probeNeighbor()
 {
  ArrayList res = new ArrayList();
  Vector v = tota.keyrdOneHop(this);
  
  test("probeNeighbor");
  testOneHop();
  
  LocalMessage m = new LocalMessage();
  m.setContent("<content="+this.id+">");
  Vector w = tota.readOneHop(m);

  if(v == null)
   return res.toArray();

  for(int i=0; i<v.size(); i++) {
   boolean valid = true;
   Tuple t = (Tuple)v.elementAt(i);

   if(w!=null) {
    for(int j=0; j<w.size();j++)
    if( ((Tuple)w.elementAt(j)).from.equals(t.from)) {
     valid = false;
     break;
    }
   }

   if(valid)
    res.add(new PeerValue(t.from,((HopTuple)t).hop));
  }
  return res.toArray();
 }

 /* This method is used to detrmine if the current is a safe state or not.
 In particular it returns 'true' if it is a safe state, 'false' otherwise.
 For this kind of tuple, a state is safe if there is a neighboring tuple having
 hop count eqaul to this hop count - 1. */
 private boolean safeState() {
  /* the source is always on a safe state */
  if(hop == 1)
   return true;
  boolean state = false;
  Object[] pv = probeNeighbor();

  for(int i=0;i<pv.length;i++) {
   if(((PeerValue)pv[i]).val == this.hop-1) {
    state = true;
    break;
   }
  }
  return state;
 }
 
 
 
 private boolean safeEnter() {
  /* the source is always on a safe state */
  if(hop == 0)
   return true;
  boolean state = false;
  Object[] pv = probeNeighbor();

  for(int i=0;i<pv.length;i++) {
   if(((PeerValue)pv[i]).val == this.hop) {
    state = true;
    break;
   }
  }
  return state;
 }
 
 

 private boolean isWorthPropagate() {
  /* count the number of neighbors */
  PresenceTuple pres = new PresenceTuple("<peer=*>");
  Vector neighbor = tota.readOneHop(pres);

  /* get my representation in the neighborhhod */
  Vector v = tota.keyrdOneHop(this);

  if(neighbor == null)
   return false;

  if(v == null)
   return true;

  /* if there are peers that do not have this tuple, it is surely worth
  propagating */
  if(neighbor.size() > v.size())
   return true;

  /* if there are peers that do have this tuple, but it is with much higher hop
  it is worth propagating, since the tuple could override the previous one */
  for(int i=0; i<v.size(); i++) {
   HopTuple other = (HopTuple)v.get(i);
   if(other.hop > (this.hop + 1))
    return true;
  }

  /* in all the other cases it is not worth propagating */
  return false;
 }
 
 
 /**
  * TEST METHODS
  *
  */
 
 private void testLocal() {
  if(tota.toString().equals(PM)) {
   int currentTime = getTime();
   if(currentTime < MIN_TIME) return;		  
   System.out.println(currentTime+" test local");
   Vector x = tota.read(new Tuple("<content=*>"));
   for(int i=0; i<x.size();i++)
	System.out.println("\t"+((Tuple)x.get(i)).serialize());
   System.out.println();	 
  }
 }
 
 private void testOneHop() {
	  if(tota.toString().equals(PM)) {
	   int currentTime = getTime();
	   if(currentTime < MIN_TIME) return;		 
	   System.out.println(currentTime+" test one hop");
	   Vector x = tota.readOneHop(new Tuple("<content=*>"));
	   for(int i=0; i<x.size();i++)
		System.out.println("\t"+((Tuple)x.get(i)).serialize());
	   System.out.println();	 
	  }
	 }
 
 private int getTime() {
	 SensorTuple st = new SensorTuple("<sensor=clock><value=*>");
	   st = (SensorTuple)tota.keyrd(st);
	   int currentTime = Integer.parseInt(st.value);
	   return currentTime;
 }
 
 private void test(String label) {
  if(tota.toString().equals(PM)) {
   int currentTime = getTime();
   if(currentTime < MIN_TIME) return;	
   System.out.println(currentTime+": "+label);
  }
 }
 
}
